! Copyright (c) 2025 ZUO Zhihua (zuo.zhihua@qq.com)
!
! Permission is hereby granted, free of charge, to any person obtaining a copy of
! this software and associated documentation files (the "Software"), to deal in
! the Software without restriction, including without limitation the rights to
! use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
! the Software, and to permit persons to whom the Software is furnished to do so,
! subject to the following conditions:
!
! The above copyright notice and this permission notice shall be included in all
! copies or substantial portions of the Software.
!
! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
! FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
! COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
! IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
! CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

!> CSV 文件写入模块:
!> 提供了一个简单的 CSV 文件写入器类型，用于将数据以 CSV 格式写入文件
!>
!> 使用示例:
!> ```fortran
!> type(csv_writer_type) :: writer
!> call writer%open_file("data.csv")
!> call writer%write_header(["x", "y", "z"])
!> call writer%write_data([1.0, 2.0, 3.0])
!> ```
module csv_module

  use stdlib_strings, only: to_string
  implicit none
  private

  public :: csv_writer_type

  !> CSV 写入器, 仅支持浮点型数据输出
  type csv_writer_type
    private
    integer :: fh
    character(:), allocatable :: header_fmt
    character(:), allocatable :: data_fmt
  contains
    procedure :: open_file, write_header, write_data
    procedure, private :: close_file
    final :: finalizer
  end type csv_writer_type

contains

  !> 打开文件
  ! filename 文件名
  ! unit_length 单元长度, 默认为 10, 只限输入 >= 10 的整数
  subroutine open_file(self, filename, unit_length)
    class(csv_writer_type), intent(inout) :: self
    character(*), intent(in) :: filename
    integer, intent(in), optional :: unit_length    !< 数据单元的输出宽度
    integer :: unit_length_

    call self%close_file()

    open (newunit=self%fh, file=filename, status='replace', action='write')
    if (present(unit_length)) then
      unit_length_ = max(unit_length, 10)
    else
      unit_length_ = 10
    end if

    allocate (self%header_fmt, source="(*(a"//to_string(unit_length_)//",:,','))")
    allocate (self%data_fmt, source="(*(es"//to_string(unit_length_)//"."//to_string(unit_length_ - 7)//",:,','))")

  end subroutine open_file

  !> 写入表头
  subroutine write_header(self, header)
    class(csv_writer_type), intent(in) :: self
    class(*), intent(in) :: header(:)

    select type (header)
    type is (character(*))  !#HACK 应对 GCC Fortran 对字符串的处理的 BUG
      write (self%fh, self%header_fmt) adjustr(header)
    class default
      error stop "Unsupported header type"
    end select

  end subroutine write_header

  !> 写入数据
  subroutine write_data(self, data)
    class(csv_writer_type), intent(in) :: self
    real, intent(in) :: data(:)

    write (self%fh, self%data_fmt) data

  end subroutine write_data

  !> 关闭文件
  subroutine close_file(self)
    class(csv_writer_type), intent(inout) :: self
    logical :: is_open
    integer :: iostat

    inquire (unit=self%fh, opened=is_open, iostat=iostat)
    if (iostat == 0) then
      if (is_open) close (self%fh)
    end if
    if (allocated(self%header_fmt)) deallocate (self%header_fmt)
    if (allocated(self%data_fmt)) deallocate (self%data_fmt)

  end subroutine close_file

  !> 释放资源
  subroutine finalizer(self)
    type(csv_writer_type), intent(inout) :: self

    call self%close_file()

  end subroutine finalizer

end module csv_module
